package br.ufrj.dcc.xml.dynamicintervals.xpath;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.swing.JTextArea;

import br.ufrj.dcc.xml.database.ItemManager;
import br.ufrj.dcc.xml.dynamicintervals.decoder.StringDecoder;
import br.ufrj.dcc.xml.dynamicintervals.vo.Item;

public class XPath {

	/**
	 * @param pane 
	 * @param args
	 * @throws SQLException 
	 * @throws IOException 
	 */
	
	
	//TODO: tratar aspas decentemente.
	
	
//	public static void main(String[] args) {
		/*
		 * TODOS OS CASOS ABAIXO FORAM TESTADOS NO XPath Visualizer.
		 * 
		 * Não aceitamos:
		 * 			".", ".."
		 * Todos acima são fáceis de se implementar, mas não houve tempo hábil
		 */
		//String path = "/disciplinas[disciplina/nome = \"AD\" and disciplina/@vagas = 60]//@vagas";
		//String path = "//*/*";
		//String path = "/disciplinas[ //nome = \"AD\" and //@vagas = 60]//@vagas";
		//String path = "/disciplinas/disciplina[@vagas != 60]//@vagas";
//		String path = "/disciplinas/disciplina[@vagas > 30]//@vagas";
		
//		String query = generateQuery(path, 2);
//		System.out.println(query);

//	}
	
	
	public static String runXPath(String path, int idDoc, JTextArea pane) throws IOException, SQLException {
		ItemManager manager = new ItemManager();
		String query=generateQuery(path, idDoc);
		ResultSet rsNodes = manager.select(query);
		String result = "";

		while (rsNodes.next()) {
			Item item = new Item();
			item.setData(rsNodes.getString("data"));
			item.setL(rsNodes.getInt("l"));
			item.setR(rsNodes.getInt("r"));
			item.setType(rsNodes.getInt("type"));
			ResultSet rsXMLFragment = manager.selectFullNode(item, idDoc);
			StringDecoder dText = new StringDecoder();
			dText.setKeepFormat(false);
			dText.decodeRS(rsXMLFragment);
			
			result+=dText.getResult() + "\n\n";
		}		
		
		pane.setText(manager.getOutputText());
		
		return result; 
	}
	
	public static String generateQuery(String path, int idDoc){
		String query = "";
		String endQuery = "";
		boolean anyParent = false;
		int tipo;
		
		String[] condicionais = null;
		//String[] condicionaisAnd = null;
		//String[] condicionaisOr  =null;
		String[] arTmp = null;
		
		int tbIndex = 1;
		
		
		
		String[] partes = splitPath(path);
		for(int i = partes.length-1; i >=0; i--){
			
			String data = partes[i].trim();
			if(data.length() > 0 &&  data.charAt(0) == '@'){
				if(data.length() > 1)
					data = data.substring(1, data.length());
				tipo = 2;
			}
			else//Lembrar no caso de alteração,que um * só pode ser do tipo 1 (tag)... 
				tipo = 1;
			
			if(partes[i].equals("*"))
				anyParent = true;
			else
				anyParent = false;
			
			// /*
			//Verifico se há condicionais.
			if(data.contains("[")){
				//Contém condicionais.
				data = data.replace("[", "---&&---");
				arTmp = data.split("---&&---");
				data = arTmp[0];
				String filtro = arTmp[1];
				filtro = filtro.substring(0, filtro.length()-1); //Retiro o "]" do final.
				
				//TODO: Tratar o "or".
				condicionais = filtro.split("and");
				
				
				//condicionaisAnd = pegaAnds(filtro);
				
			}else
				condicionais = null;
			
			//*/
			
			if(i == partes.length-1){
				//Último elemento.
				query = "SELECT * FROM items i" + tbIndex  + "\n";
				if(anyParent)
					query += " WHERE i" + tbIndex + ".type = " + tipo + "\n";
				else{
					query += " WHERE i" + tbIndex + ".data = '" + data + "'" + "\n";
					query += " AND i" + tbIndex + ".type = " + tipo + "\n";
				}
				
				if(condicionais != null){
					query += adicionaCondicionais(idDoc, condicionais, tbIndex);
				}
				
			}else if(!data.trim().equals("")){
				tbIndex++;
				
				query += " AND EXISTS (" + "\n"
				+ " SELECT * " + "\n"
				+ " FROM items i" + tbIndex + "\n";
				
				//Se o partes[i+1] for vazio, significa que foram encontradas duas barras...
				//i <  partes.length-1 sempre.
				if(partes[i+1].equals("")){
					query += " WHERE i" + tbIndex + ".l = func_Ancestor(i" + (tbIndex-1) + ".l, '" + data + "', " + idDoc + ")" + "\n";
					//query += " AND i" + tbIndex + ".data = '" + data + "' " + "\n";	
				}
				else{
					query+= " WHERE i" + tbIndex + ".l = func_Parent(i" + (tbIndex-1) + ".l, " + idDoc + ")" + "\n";
					if(!anyParent)
						query += " AND i" + tbIndex + ".data = '" + data + "' " + "\n";
				}
				
				query += " AND i" + tbIndex + ".type = " + tipo + "\n";
				
				if(condicionais != null){
					query += adicionaCondicionais(idDoc, condicionais, tbIndex);
				}
				
				endQuery += " )";
				
			}else if(data.equals("") && i == 0 && partes.length > 1){
				//Se for a primeira posição do vetor teremos os casos:
				if(partes.length >= 3 && partes[1].equals("")){
					//Significa que há 2 barras no início do path.
					
				}else if(!query.equals("")){
					//Se a query não está vazia.
					//Se não há 2 barras, o último elemento acrescentado deve ser a raiz.
					query += " AND i" + tbIndex + ".l = 0 \n";
				}
			}
		}
		query += endQuery;
		return query;
	}
/*
	private static String[] pegaAnds(String filtro) {
		String[] condicionaisAnd = filtro.split(" and ");
		
		//Elimino os "or"
		for(int i=0; i<condicionaisAnd.length; i++){
			condicionaisAnd[i] = condicionaisAnd[i].split(" or ")[0];
		}
		
		
		return condicionaisAnd;
	}
	
	private static String[] pegaOrs(String filtro) {
		String[] condicionaisAnd = filtro.split(" or ");
		
		//Elimino os "or"
		for(int i=0; i<condicionaisAnd.length; i++){
			condicionaisAnd[i] = condicionaisAnd[i].split(" and ")[0];
		}
		
		
		return condicionaisAnd;
	}
*/
	private static String[] splitPath(String path) {
		String[] partes = path.split("/");
		String[] consertado = new String[partes.length];
		int j = 0;
		for(int i=0; i<partes.length; i++){
			String p = partes[i];
			
			if(consertado[j] == null){
				//É o primeiro a entrar na posição. (Novo)
				consertado[j] = p;
				
				if(! (p.contains("[") || p.contains("]")) || (p.contains("[") && p.contains("]") ) ){
					//Se não há filtros ou se os filtros são abertos e fechados. Está consertado.
					j++;
				}
				
			}else{
				//Significa que não foi fechado.
				consertado[j] += "/" + p; 
				if(p.contains("]"))
					j++;				
			}	
		}
		if(j == partes.length-1)
			return consertado;
		else{
			String[] definitivo = new String[j];
			for(int i=0; i < j; i++){
				definitivo[i] = consertado[i];
			}
			return definitivo;
		}
	}

	
	
	
	private static String adicionaCondicionais(int idDoc, String[] condicionais, int tbIndex) {
		String[] arTmp;
		String query = "";
		String endQueryExp = "";
		for(int j=0; j<condicionais.length; j++){
			String cond = condicionais[j];
			
			
			String comparador = null;
			arTmp = cond.split("!=");
			if(arTmp.length == 2){
				comparador = "<>";
			}else{
				arTmp = cond.split(">=");
				if(arTmp.length == 2)
					comparador = ">=";
				else{
					arTmp = cond.split("<=");
					if(arTmp.length == 2)
						comparador = "<=";
					else{
						arTmp = cond.split("=");
						if(arTmp.length == 2)
							comparador = "=";
						else{
							arTmp = cond.split("<");
							if(arTmp.length == 2)
								comparador = "<";
							else{
								arTmp = cond.split(">");
								if(arTmp.length == 2)
									comparador = ">";
							}
						}
					}
				}
			}
			if(comparador != null){
				endQueryExp = "";
				
				String expEsq = arTmp[0];
				String expDir = arTmp[1];
				
				
				
				int tipoExp;
				
				String[] partesExp = expEsq.split("/");
				int tbIndexExp = 0;
				
				for(int k = 0; k <partesExp.length; k++){
					String dataExp = partesExp[k].trim();
					if(dataExp.length() > 0 &&  dataExp.charAt(0) == '@'){
						if(dataExp.length() > 1)
							dataExp = dataExp.substring(1, dataExp.length());
						tipoExp = 2;
					}
					else
						tipoExp = 1;
					
					if( !dataExp.equals("") ){
						int numBarras;
						if(k>1){
							//Existem ao menos duas posições antes dele.
							if(partesExp[k-1].trim().equals("")){
								//Se entre duas posições há um espaço vazio, temos duas barras entre elas.
								numBarras = 2;
							}else{
								numBarras = 1;
							}
						}else{
							if(k==1)
								numBarras = 1;
							else
								numBarras = 0;
						}
						
						
						tbIndexExp++;
						String tb = "i" +  tbIndex + "_" + j + "_" + tbIndexExp;
						String tbPai = "i" +  tbIndex;
						if(tbIndexExp > 1)
							tbPai +=  "_" + j + "_" + (tbIndexExp-1);
						
						query += " AND EXISTS (\n"
							+ " SELECT * FROM items " + tb + "\n";
						if(numBarras == 0 || (numBarras == 1 && (k>1 || !partesExp[k-1].trim().equals("")) ) ){
							//Se não há barras antes (é o primeiro elemento) e se houver apenas uma tenho que verificar se 
							//o seu antecessor não é o primeiro, caso seja, ele não pode estar vazio ou implicará em começarmos da raiz.
							
							//Ele então é filho do seu antecessor.
							query+= " WHERE " + tbPai + ".l = func_Parent(" + tb + ".l, " + idDoc + ")" + "\n";
						}else if(numBarras == 2 && partesExp[k-2].trim().equals("")){
							//Se tem 2 barras no início.
							//Significa que ele não precisa ter sucessor.
							query+= " WHERE 1=1";
							
						}else if(numBarras == 1){
							//Ele tem que ser a raiz do documento.
							query+= " WHERE " + tb + ".l = 0";
						}else{
							//só pode ser numBarras = 2.
							query+= " WHERE " + tbPai + ".l = func_Ancestor(" + tb + ".l, '" + partesExp[k-2].trim() + "', " + idDoc + ")" + "\n";
						}
						
						query += " AND " + tb + ".data = '" + dataExp.trim() + "' " + "\n";
						query += " AND " + tb + ".type = " + tipoExp + "\n";
						
						if(k == partesExp.length-1){
							//Aqui que entra a comparação do filtro.
							
							tbIndexExp++;
							String tbAtt = "i" +  tbIndex + "_" + j + "_" + tbIndexExp;
							int tipoDir;
							if(tipoExp == 2)
								tipoDir = 3;
							else
								tipoDir = 4;
							query += " AND EXISTS (\n"
								+ " SELECT * FROM items " + tbAtt + "\n";									
							query+= " WHERE " + tb + ".l = func_Parent(" + tbAtt + ".l, " + idDoc + ")" + "\n";
							query += " AND " + tbAtt + ".data " + comparador + " '" + retiraAspas(expDir.trim()) + "' " + "\n";
							query += " AND " + tbAtt + ".type = " + tipoDir + "\n";
							
							endQueryExp += " )\n)\n";
						}else{
							
							endQueryExp += " )\n";
							
						}
					}
				}
				
				query += endQueryExp;
				
			}
			
		}
		
		return query;
	}

	private static String retiraAspas(String expDir) {
		String ret = expDir.replace("'", "");
		ret = ret.replace("\"", "");
		return ret;
	}

}
